package com.founder.gateway.component.transport.netty.http;

import static com.founder.gateway.system.util.HttpNormUtil.APPEND_UTF_8;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.setu.framework.thread.SetuThreadLocalHandler;
import org.setu.framework.util.SetuApplicationUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.founder.gateway.component.transport.netty.filter.NettyFilterChain;
import com.founder.gateway.component.transport.netty.filter.NettyFilterChainManager;
import com.founder.gateway.component.transport.sentinel.SentinelConstants;
import com.founder.gateway.component.transport.sentinel.SentinelServer;
import com.founder.gateway.orm.fzapi.bean.entity.ApiEntityEntity;
import com.founder.gateway.orm.fzapi.bean.entity.ApiSchemeEntity;
import com.founder.gateway.service.ApiService;
import com.founder.gateway.system.bean.SetuResult;
import com.founder.gateway.system.util.HttpNormUtil;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpUtil;

/**
 * @author hfl
 * 2018年12月4日
 */
public class NettyHttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final Logger log = LoggerFactory.getLogger(NettyHttpHandler.class);
    @SuppressWarnings("rawtypes")
    private static final Map<String, ReadableDataSource> schemeMap = new ConcurrentHashMap<String, ReadableDataSource>();
    private static ConfigService configService;

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest)
            throws Exception {
        NettyHttpRequest request = new NettyHttpRequest(fullHttpRequest);
        NettyHttpResponse response = new NettyHttpResponse(new DefaultFullHttpResponse(
                HTTP_1_1, OK, Unpooled.directBuffer(16, 20971520)));
        request.setRemoteAddr(HttpNormUtil.getRemoteIp(request, ctx));
        String apiSchemeId = HttpNormUtil.getLastPart(request.uri());
        NettyFilterChain chain = new NettyFilterChain();
        if(request.uri().contains(".")) {
    	   response.content().clear().writeBytes(new SetuResult(500, HttpNormUtil.NOT_FOUND ).toString().getBytes());
           response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + APPEND_UTF_8);
           return;
        }
        try {
        	SetuThreadLocalHandler.initAll();
            ApiSchemeEntity apiSchemeEntity = SetuApplicationUtil.getBean(ApiService.class).queryApiScheme(apiSchemeId);
            ApiEntityEntity apiEntityEntity = SetuApplicationUtil.getBean(ApiService.class).queryApi(apiSchemeEntity.getApiEntityId());
            //TODO 根据用户请求的方案   动态加载过滤链
            chain.setApiSchemeEntity(apiSchemeEntity);
            chain.setApiEntityEntity(apiEntityEntity);
            chain.registerFilter(NettyFilterChainManager.getFilter("base"));
            chain.registerFilter(NettyFilterChainManager.getFilter("authentication"));
            chain.registerFilter(NettyFilterChainManager.getFilter("requestParam"));
            chain.registerFilter(NettyFilterChainManager.getFilter("sentinel"));
            chain.registerFilter(NettyFilterChainManager.getFilter("responseParam"));
            chain.registerFilter(NettyFilterChainManager.getFilter("automaticEntity"));
            chain.registerFilter(NettyFilterChainManager.getFilter("protocol"));
            chain.registerFilter(NettyFilterChainManager.getFilter("request"));
            //TODO　集群管理
            @SuppressWarnings("unchecked")
            ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = schemeMap.get(apiSchemeId);
            if (flowRuleDataSource == null) {
                try {
                    if (configService == null) {
                        configService = NacosFactory.createConfigService(SentinelServer.address);
                    }
                    String dataId = apiSchemeEntity.getId() + SentinelConstants.FLOW_POSTFIX;
                    String groupId = apiEntityEntity.getGroupName() == null ? SentinelServer.groupId : apiEntityEntity.getGroupName();
                    if (configService.getConfig(dataId, groupId, 3000) == null) {
                        //TODO  以后做集群处理
                        final String rule = "[\n"
                                + "  {\n"
                                + "    \"resource\": \"" + apiSchemeId + "\",\n"
                                + "    \"controlBehavior\": 0,\n"
                                + "    \"count\": " + apiSchemeEntity.getLimitCount() + ".0,\n"
                                + "    \"grade\": 1,\n"
                                + "    \"limitApp\": \"default\",\n"
                                + "    \"strategy\": 0\n"
                                + "  }\n"
                                + "]";
                        configService.publishConfig(dataId, groupId, rule, apiEntityEntity.getAppName());
                    }
                    flowRuleDataSource = new NacosDataSource<>(SentinelServer.address, groupId, dataId,
                            source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                            }));
                    schemeMap.put(apiSchemeId, flowRuleDataSource);
                } catch (NacosException e) {
                    e.printStackTrace();
                }
            }
            FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
            chain.doFilter(request, response);
        } catch (Exception e) {
            response.content().clear().writeBytes(new SetuResult(500, HttpNormUtil.NOT_FOUND + e.getMessage()).toString().getBytes());
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + APPEND_UTF_8);
        }finally {
        	SetuThreadLocalHandler.clearAll();
		}

        // keep-alive 的处理
        if (HttpUtil.isKeepAlive(request)) {
            HttpUtil.setKeepAlive(response, true);
            if (!HttpUtil.isContentLengthSet(response)) {
                HttpUtil.setContentLength(response, response.content().writerIndex());
            }
            ctx.write(ctx.write(response.getHttpResponse()));
        } else {
            ctx.write(ctx.write(response.getHttpResponse())).addListener(ChannelFutureListener.CLOSE);
        }
        ctx.flush();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        log.error(cause.toString());
        ctx.close();
    }
}
